package com.example.generator.controller;

import com.example.generator.model.TableInfo;
import com.example.generator.service.CodeGeneratorService;
import com.example.generator.service.DatabaseService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Api(tags = "代码生成器接口")
@RestController
@RequestMapping("/api/generator")
public class GeneratorController {

    private static final Logger log = LoggerFactory.getLogger(GeneratorController.class);

    public static final String TABLE_NAME = "tableName";

    @Autowired
    private CodeGeneratorService generatorService;

    @Autowired
    private DatabaseService databaseService;

    @ApiOperation("获取所有数据库表")
    @GetMapping("/tables")
    public List<Map<String, Object>> getTables() {
        return databaseService.getAllTables();
    }

    @ApiOperation("预览生成的代码")
    @PostMapping("/preview")
    public Map<String, String> previewCode(@RequestParam String tableName,
                                         @RequestParam String author,
                                         @RequestParam String basePackage) {
        String tempDir = "temp/" + System.currentTimeMillis();
        try {
            // 创建临时目录
            new File(tempDir).mkdirs();

            // 生成代码
            generatorService.generateTableCode(tableName, author, basePackage, tempDir);

            // 读取生成的文件内容
            Map<String, String> fileContents = new HashMap<>();
            Files.walk(Paths.get(tempDir))
                .filter(Files::isRegularFile)
                .forEach(path -> {
                    try {
                        String relativePath = Paths.get(tempDir).relativize(path).toString();
                        String content = new String(Files.readAllBytes(path));
                        fileContents.put(relativePath, content);
                    } catch (IOException e) {
                        log.error("读取文件失败: " + path, e);
                    }
                });

            return fileContents;
        } catch (Exception e) {
            log.error("代码预览失败", e);
            throw new RuntimeException("代码预览失败", e);
        } finally {
            // 清理临时文件
            deleteDirectory(new File(tempDir));
        }
    }

    @ApiOperation("预览选中表的代码")
    @PostMapping("/preview/selected")
    public Map<String, Map<String, String>> previewSelectedCode(@RequestParam String tables,
                                                              @RequestParam String author,
                                                              @RequestParam String basePackage) {
        String tempDir = "temp/" + System.currentTimeMillis();
        try {
            // 创建临时目录
            new File(tempDir).mkdirs();

            // 分割表名
            String[] tableNames = tables.split(",");

            // 存储每个表的文件内容
            Map<String, Map<String, String>> allContents = new HashMap<>();

            // 生成每个表的代码
            for (String tableName : tableNames) {
                String tableDir = tempDir + "/" + tableName;
                new File(tableDir).mkdirs();

                try {
                    generatorService.generateTableCode(tableName, author, basePackage, tableDir);

                    // 读取生成的文件内容
                    Map<String, String> fileContents = new HashMap<>();
                    Files.walk(Paths.get(tableDir))
                        .filter(Files::isRegularFile)
                        .forEach(path -> {
                            try {
                                String relativePath = Paths.get(tableDir).relativize(path).toString();
                                String content = new String(Files.readAllBytes(path));
                                fileContents.put(relativePath, content);
                            } catch (IOException e) {
                                log.error("读取文件失败: " + path, e);
                            }
                        });

                    allContents.put(tableName, fileContents);
                } catch (Exception e) {
                    log.error("生成表 " + tableName + " 的代码失败", e);
                }
            }

            return allContents;
        } catch (Exception e) {
            log.error("代码预览失败", e);
            throw new RuntimeException("代码预览失败", e);
        } finally {
            // 清理临时文件
            deleteDirectory(new File(tempDir));
        }
    }

    @ApiOperation("生成代码")
    @PostMapping("/generate")
    public void generateCode(@RequestParam String tableName,
                           @RequestParam String author,
                           @RequestParam String basePackage,
                           @RequestParam(defaultValue = "true") boolean preserveStructure,
                           @RequestParam(defaultValue = "true") boolean generateTest,
                           HttpServletResponse response) {
        String tempDir = "temp/" + System.currentTimeMillis();
        File tempDirFile = new File(tempDir);
        File zipFile = new File(tempDir + "/" + tableName + "_code.zip");

        try {
            // 创建临时目录
            tempDirFile.mkdirs();

            // 生成代码
            generatorService.generateTableCode(tableName, author, basePackage, tempDir);

            // 创建ZIP文件
            try (FileOutputStream fos = new FileOutputStream(zipFile);
                 ZipOutputStream zos = new ZipOutputStream(fos)) {
                addDirectoryToZip(tempDirFile, preserveStructure ? "" : null, zos);
            }

            // 设置响应头
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=" + tableName + "_code.zip");
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Content-Length", String.valueOf(zipFile.length()));

            // 使用NIO进行高效文件传输
            Path path = zipFile.toPath();
            Files.copy(path, response.getOutputStream());
            response.flushBuffer();

        } catch (Exception e) {
            log.error("代码生成失败", e);
            response.reset();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            try {
                response.getWriter().write("生成代码失败: " + e.getMessage());
            } catch (IOException ex) {
                log.error("发送错误响应失败", ex);
            }
        } finally {
            // 确保清理临时文件
            try {
                if (zipFile.exists()) {
                    Files.delete(zipFile.toPath());
                }
                deleteDirectory(tempDirFile);
            } catch (IOException e) {
                log.error("清理临时文件失败", e);
            }
        }
    }

    @ApiOperation("生成所有表的代码")
    @PostMapping("/generate/all")
    public void generateAllCode(@RequestParam String author,
                              @RequestParam String basePackage,
                              @RequestParam(defaultValue = "true") boolean preserveStructure,
                              @RequestParam(defaultValue = "true") boolean generateTest,
                              HttpServletResponse response) {
        String tempDir = "temp/" + System.currentTimeMillis();
        try {
            // 创建临时目录
            new File(tempDir).mkdirs();

            // 获取所有表信息
            List<Map<String, Object>> tables = databaseService.getAllTables();

            // 生成所有表的代码
            for (Map<String, Object> table : tables) {
                String tableName = (String) table.get(TABLE_NAME);
                log.info("正在生成表 {} 的代码", tableName);
                try {
                    generatorService.generateTableCode(tableName, author, basePackage, tempDir);
                    if (generateTest) {
                        TableInfo tableInfo = databaseService.getTableInfo(tableName);
                        generatorService.generateEntityTest(tableInfo);
                    }
                } catch (Exception e) {
                    log.error("生成表 {} 的代码失败", tableName, e);
                }
            }

            // 创建ZIP文件
            String zipFile = tempDir + "/all_tables_code.zip";
            try (FileOutputStream fos = new FileOutputStream(zipFile);
                 ZipOutputStream zos = new ZipOutputStream(fos)) {
                addDirectoryToZip(new File(tempDir), preserveStructure ? "" : null, zos);
            }

            // 设置响应头
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=all_tables_code.zip");

            // 写入响应流
            try (FileInputStream fis = new FileInputStream(zipFile);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                byte[] buffer = new byte[1024];
                int i;
                while ((i = bis.read(buffer)) != -1) {
                    response.getOutputStream().write(buffer, 0, i);
                }
                response.getOutputStream().flush();
            }

            // 清理临时文件
            deleteDirectory(new File(tempDir));
        } catch (Exception e) {
            log.error("生成所有表代码失败", e);
            throw new RuntimeException("生成所有表代码失败", e);
        }
    }

    @ApiOperation("生成选中表的代码")
    @PostMapping("/generate/selected")
    public void generateSelectedCode(@RequestParam String tables,
                                   @RequestParam String author,
                                   @RequestParam String basePackage,
                                   @RequestParam(defaultValue = "true") boolean preserveStructure,
                                   HttpServletResponse response) {
        String tempDir = "temp/" + System.currentTimeMillis();
        try {
            // 创建临时目录
            new File(tempDir).mkdirs();

            // 分割表名
            String[] tableNames = tables.split(",");

            // 生成选中表的代码
            for (String tableName : tableNames) {
                log.info("正在生成表 {} 的代码", tableName);
                try {
                    generatorService.generateTableCode(tableName, author, basePackage, tempDir);
                } catch (Exception e) {
                    log.error("生成表 {} 的代码失败", tableName, e);
                }
            }

            // 创建ZIP文件
            String zipFile = tempDir + "/selected_tables_code.zip";
            try (FileOutputStream fos = new FileOutputStream(zipFile);
                 ZipOutputStream zos = new ZipOutputStream(fos)) {
                addDirectoryToZip(new File(tempDir), preserveStructure ? "" : null, zos);
            }

            // 设置响应头
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=selected_tables_code.zip");

            // 写入响应流
            try (FileInputStream fis = new FileInputStream(zipFile);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                byte[] buffer = new byte[1024];
                int i;
                while ((i = bis.read(buffer)) != -1) {
                    response.getOutputStream().write(buffer, 0, i);
                }
                response.getOutputStream().flush();
            }

            // 清理临时文件
            deleteDirectory(new File(tempDir));
        } catch (Exception e) {
            log.error("生成选中表代码失败", e);
            throw new RuntimeException("生成选中表代码失败", e);
        }
    }

    @ApiOperation("下载生成的文件夹")
    @PostMapping("/download/folder")
    public void downloadFolder(@RequestParam String path, HttpServletResponse response) {
        try {
            File folder = new File(path);
            if (!folder.exists() || !folder.isDirectory()) {
                throw new RuntimeException("文件夹不存在");
            }

            // 设置响应头
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=generated_code.zip");

            // 创建临时ZIP文件
            String zipFile = path + "_download.zip";
            try (FileOutputStream fos = new FileOutputStream(zipFile);
                 ZipOutputStream zos = new ZipOutputStream(fos)) {
                addDirectoryToZip(folder, "", zos);
            }

            // 写入响应流
            try (FileInputStream fis = new FileInputStream(zipFile);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                byte[] buffer = new byte[1024];
                int i;
                while ((i = bis.read(buffer)) != -1) {
                    response.getOutputStream().write(buffer, 0, i);
                }
                response.getOutputStream().flush();
            }

            // 清理临时文件
            new File(zipFile).delete();
            deleteDirectory(folder);
        } catch (Exception e) {
            log.error("下载文件夹失败", e);
            throw new RuntimeException("下载文件夹失败", e);
        }
    }

    /**
     * 递归添加目录到ZIP
     * @param basePath 为null时使用扁平化结构，否则保持目录结构
     */
    private void addDirectoryToZip(File directory, String basePath, ZipOutputStream zos) throws IOException {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    String path = basePath == null ? "" : (basePath.isEmpty() ? file.getName() : basePath + "/" + file.getName());
                    addDirectoryToZip(file, path, zos);
                } else {
                    String path;
                    if (basePath == null) {
                        // 扁平化结构：直接使用文件名
                        path = file.getName();
                    } else {
                        // 保持目录结构
                        path = basePath.isEmpty() ? file.getName() : basePath + "/" + file.getName();
                    }
                    ZipEntry zipEntry = new ZipEntry(path);
                    zos.putNextEntry(zipEntry);

                    try (FileInputStream fis = new FileInputStream(file)) {
                        byte[] buffer = new byte[1024];
                        int length;
                        while ((length = fis.read(buffer)) > 0) {
                            zos.write(buffer, 0, length);
                        }
                    }
                    zos.closeEntry();
                }
            }
        }
    }

    /**
     * 递归删除目录及其内容
     */
    private void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }
}
